<?php

/**
 * 享元模式（实现对象的复用）
 * 
 * 概述：运用共享技术有效地支持大量细粒度对象的复用。
 *  	（系统只使用少量的对象，而这些对象都很相似，状态变化很小，可以实现对象的多次复用。
 *  	由于享元模式要求能够共享的对象必须是细粒度对象，因此它又称为轻量级模式，它是一种对象结构型模式。）
 * 
 * 类型：结构型模式
 * 
 * 角色：
 *  1，抽象享元类。
 *  2，具体享元类。
 *  3，非共享具体享元类。
 *  4，享元工厂类。
 *  
 * 总结：当系统中存在大量相同或者相似的对象时，享元模式是一种较好的解决方案，
 *  	它通过共享技术实现相同或相似的细粒度对象的复用，从而节约了内存空间，提高了系统性能。
 *  	相比其他结构型设计模式，享元模式的使用频率并不算太高，但是作为一种以“节约内存，
 *  	提高性能”为出发点的设计模式，它在软件开发中还是得到了一定程度的应用。
 *  
 * 优点：
 *  1，可以极大减少内存中对象的数量，使得相同或相似对象在内存中只保存一份，从而可以节约系统资源，提高系统性能。
 *  2，享元模式的外部状态相对独立，而且不会影响其内部状态，从而使得享元对象可以在不同的环境中被共享。
 *  
 * 缺点：
 *  1，享元模式使得系统变得复杂，需要分离出内部状态和外部状态，这使得程序的逻辑复杂化。
 *  2，为了使对象可以共享，享元模式需要将享元对象的部分状态外部化，而读取外部状态将使得运行时间变长。
 * 
 * 适用场景：
 *  1，一个系统有大量相同或者相似的对象，造成内存的大量耗费。
 *  2，对象的大部分状态都可以外部化，可以将这些外部状态传入对象中。
 *  3，在使用享元模式时需要维护一个存储享元对象的享元池，而这需要耗费一定的系统资源，
 *  因此，应当在需要多次重复使用享元对象时才值得使用享元模式。
 * 
 * 其它：
 * 	1，单纯享元模式。所有的具体享元类都是可以共享的，不存在非共享具体享元类。
 *  2，复合享元模式。将一些单纯享元对象使用组合模式加以组合，还可以形成复合享元对象，
 *  这样的复合享元对象本身不能共享，但是它们可以分解成单纯享元对象，而后者则可以共享。
 */

/**
 * 抽象享元类
 */
abstract class Flyweight
{
	/**
	 * 外部状态
	 */
	abstract public function operation($state);
}

/**
 * 具体享元类
 */
class ConcreteFlyweight extends Flyweight
{
	private $_intrinsicState;
	
	/**
	 * 内部状态
	 */
	public function __construct($state)
	{
		$this->_intrinsicState = $state;
	}
	
	
	public function operation($state)
	{
		echo 'ConcreteFlyweight operation, Intrinsic State = ' . $this->_intrinsicState
        . ' Extrinsic State = ' . $state . '<br />';
	}
}

/**
 * 非共享具体享元类
 */
class UnsharedConcreteFlyweight extends Flyweight
{
	private $_intrinsicState;
	
	/**
	 * 内部状态
	 */
	public function __construct($state)
	{
		$this->_intrinsicState = $state;
	}
	
	
	public function operation($state)
	{
		echo 'UnsharedConcreteFlyweight operation, Intrinsic State = ' . $this->_intrinsicState
        . ' Extrinsic State = ' . $state . '<br />';
	}
}

/**
 * 享元工厂类
 */
class FlyweightFactory
{
	private $_flyweights;
	
	public function __construct()
	{
		$this->_flyweights = array();
	}
	
	public function getFlyweight($state)
	{
		if (isset($this->_flyweights[$state])) {
			return $this->_flyweights[$state];
		}
		return $this->_flyweights[$state] = new ConcreteFlyweight($state);
	}
}

class Client
{
	public static function main()
	{
		$factory = new FlyweightFactory();
		
		$flyweight = $factory->getFlyweight('state A');
		$flyweight->operation('other state A');
		
		$flyweight = $factory->getFlyweight('state B');
		$flyweight->operation('other state B');
		
		//不共享对象，单独调用
		$uflyweight = new UnsharedConcreteFlyweight('state A');
		$uflyweight->operation('other state A');
	}
}

Client::main();